const Proxy = require('../../proxy')
const webDataUtil = require('../../util/webDataUtil')
const reqlib = require('app-root-path').require
const btbAs = reqlib('/app/common/btb')
const appConfig = reqlib('/config/index')
const commonDataCreate = require('./utils/commonDataCreate')
const AnalysisInfoFlag = reqlib('/app/analysis/analysisInfoFlag')
const AccountMachine = reqlib('/app/analysis/accountMachine')
const BuySellMachine = reqlib('/app/analysis/buySellMachine')
const binanceProxy = reqlib('/app/common/binanceProxy')
const moment = require('moment')

const BtbIndex = Proxy.BtbIndex
const Dictionary = Proxy.Dictionary
const QuoteVolumeProxy = Proxy.QuoteVolume

const sleep = async (time) => {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve()
    }, time)
  })
}

const getBtbListWithKlineList = async () => {
  let btbList = await BtbIndex.find({
    status: 1,
    hy_status: 1
  }, {
    sort: '-proportion',
    lean: true
  })
  btbList = btbList.filter((v) => {
    return !v.manual && !v.blacklist
  })
  const newList = []
  for (let i = 0; i < btbList.length; i++) {
    const item = btbList[i]
    const key = item.code
    const klineResult = await Promise.all([
      webDataUtil.getFuturesKLinesAll({
        name: `${key}`,
        interval: '15m',
        count: 500
      }),
      webDataUtil.getFuturesKLinesAll({
        name: `${key}`,
        interval: '1h',
        count: 500
      }),
      webDataUtil.getFuturesKLinesAll({
        name: `${key}`,
        interval: '4h',
        count: 500
      }),
      webDataUtil.getFuturesKLinesAll({
        name: `${key}`,
        interval: '1d',
        count: 500
      })
    ])
    item.symbol = `${item.code}USDT`
    item.min_qty = item.f_min_qty
    item.max_qty = item.f_max_qty
    item.step_size = item.f_step_size
    item.min_notional = item.f_min_notional
    item.tick_size = item.f_tick_size
    item.klineList15m = klineResult[0]
    item.klineList1h = klineResult[1]
    item.klineList4h = klineResult[2]
    item.klineList1d = klineResult[3]
    newList.push(item)
  }
  return newList
}

const analysisBtbList = (btbList) => {
  for (let i = 0; i < btbList.length; i++) {
    const item = btbList[i]
    // 慢是因为分析2000天的，10个就20000天
    item.analysisInfoList15m = btbAs.getItemInfoList(item.klineList15m)
    item.analysisInfoList1h = btbAs.getItemInfoList(item.klineList1h)
    item.analysisInfoList4h = btbAs.getItemInfoList(item.klineList4h)
    item.analysisInfoList1d = btbAs.getItemInfoList(item.klineList1d)
    // 都转为从近到远
    item.analysisInfoList15m.reverse()
    item.analysisInfoList1h.reverse()
    item.analysisInfoList4h.reverse()
    item.analysisInfoList1d.reverse()
    item.analysisInfoList15m.forEach((v, index) => {
      commonDataCreate.asInfoExtend(item.analysisInfoList15m, v, index)
    })
    item.analysisInfoList1h.forEach((v, index) => {
      commonDataCreate.asInfoExtend(item.analysisInfoList1h, v, index)
    })
    item.analysisInfoList4h.forEach((v, index) => {
      commonDataCreate.asInfoExtend(item.analysisInfoList4h, v, index)
    })
    item.analysisInfoList1d.forEach((v, index) => {
      commonDataCreate.asInfoExtend(item.analysisInfoList1d, v, index)
    })
    item.klineList15m.reverse()
    item.klineList1h.reverse()
    item.klineList4h.reverse()
    item.klineList1d.reverse()
    item.analysisInfoFlag15m = new AnalysisInfoFlag({
      analysisInfoList: item.analysisInfoList15m,
      type: '15m',
      code: item.code
    })
    item.close = item.analysisInfoFlag15m.nowEmaInfo.close
    item.analysisInfoFlag1h = new AnalysisInfoFlag({
      analysisInfoList: item.analysisInfoList1h,
      type: '1h',
      code: item.code
    })
    item.analysisInfoFlag4h = new AnalysisInfoFlag({
      analysisInfoList: item.analysisInfoList4h,
      type: '4h',
      code: item.code
    })
    item.analysisInfoFlag1d = new AnalysisInfoFlag({
      analysisInfoList: item.analysisInfoList1d,
      type: '1d',
      code: item.code
    })
  }
}

const sortBtbIndexList = (btbList) => {
  // 平仓的放前面，这样可以腾挪出资金
  const newList = []
  const pingList = []
  const hasPositionList = []
  btbList.forEach((btbItem) => {
    const buySellMachineInfo = btbItem.buySellMachine
    if (
      !(buySellMachineInfo.buy || buySellMachineInfo.sell) &&
      (
        (buySellMachineInfo.pingKong && buySellMachineInfo.hasKongPosition) ||
        (buySellMachineInfo.pingDuo && buySellMachineInfo.hasDuoPosition)
      )
    ) {
      // 先平
      pingList.push(btbItem)
    } else if (buySellMachineInfo.hasPosition) {
      // 再维护有仓位的
      hasPositionList.push(btbItem)
    } else {
      // 新添加的
      newList.push(btbItem)
    }
  })
  const realBuySellList = [...newList, ...hasPositionList]
  // 优先买卖新的币，而不是原来的继续加仓
  realBuySellList.sort((a, b) => {
    // 返回1，a到b右边
    // 返回-1，b到a右边
    const openInfoa = a.buySellMachine
    const openInfob = b.buySellMachine
    openInfoa.buySellPriority = openInfoa.buySellPriority || 0
    openInfob.buySellPriority = openInfob.buySellPriority || 0
    return openInfob.buySellPriority - openInfoa.buySellPriority
  })
  return [...pingList, ...realBuySellList]
}

const getDictionaryInfo = async () => {
  const preFind = await Dictionary.findOne({
    key: 'preFindBtbResult'
  })
  let preFindBtbResult = {}
  if (preFind) {
    try {
      preFindBtbResult = JSON.parse(preFind.value)
    } catch (e) {
      preFindBtbResult = {}
    }
  }
  // 仓位配置
  let btbPositionConfigRes = await Dictionary.findOne({ key: 'btbPositionConfig' })
  let btbPositionConfig = (btbPositionConfigRes && btbPositionConfigRes.value) || 100
  let btbPositionConfigRaw = btbPositionConfig
  if (btbPositionConfig !== '自动') {
    btbPositionConfig = parseInt(btbPositionConfig) || 100
    btbPositionConfig = btbPositionConfig / 100
  } else {
    btbPositionConfig = preFindBtbResult.positionConfig || 0.5
  }
  return {
    preFindBtbResult,
    btbPositionConfig,
    btbPositionConfigRaw: btbPositionConfigRaw
  }
}

const getRecentMarketInfo = async () => {
  const list = await QuoteVolumeProxy.find({}, {
    skip: 0,
    limit: 200,
    sort: '-time'
  }) || []
  return list.map((v) => {
    const detail = v.detail || {}
    return {
      ...detail
    }
  })
}

// 取消止盈单
const requestCancelStopOrder = async () => {
  const orderList = await binanceProxy.futuresAllOrders()
  try {
    for (let i = 0; i < orderList.length; i++) {
      const item = orderList[i]
      await binanceProxy.futuresCancelAll(item.symbol)
    }
  } catch (e) {
    console.log(e)
  }
}

// 获取合约账户财务信息
module.exports = async function (ctx) {
  const btbOpen = await Dictionary.findOne({ key: 'btbOpen' })
  if (!(btbOpen && btbOpen.value === 'true')) {
    return false
  }
  const dictionaryInfo = await getDictionaryInfo()
  const marketInfoList = await getRecentMarketInfo()
  let preFindBtbResult = dictionaryInfo.preFindBtbResult
  let btbPositionConfig = dictionaryInfo.btbPositionConfig
  let btbPositionConfigRaw = dictionaryInfo.btbPositionConfigRaw
  // 先取消限价单，收回保证金
  await requestCancelStopOrder()
  // const p = new Performance()
  // p.start()
  let btbList = await getBtbListWithKlineList()
  let POSITION_LEN = appConfig.btb.minBtbNumber
  if (btbList.length > POSITION_LEN) {
    POSITION_LEN = btbList.length
  }
  if (POSITION_LEN > appConfig.btb.maxBtbNumber) {
    POSITION_LEN = appConfig.btb.maxBtbNumber
  }
  // p.print('kline数据')
  analysisBtbList(btbList)
  // 获取金额
  const accountMachine = new AccountMachine({
    btbCount: POSITION_LEN,
    positionConfig: btbPositionConfig,
    dictionaryPosition: btbPositionConfigRaw,
    marketInfo: preFindBtbResult,
    marketInfoList: marketInfoList || []
    // btbCount: btbList.length > 8 ? btbList.length : 8
  })
  await accountMachine.refresh()
  // 计算出买卖信号
  btbList.forEach((v) => {
    const symbolAccount = accountMachine.getBtbItemAccount(v.symbol)
    const futuresAccount = accountMachine.getFuturesAccount()
    v.buySellMachine = new BuySellMachine({
      realBuySell: true,
      btbItem: v,
      btbCount: POSITION_LEN,
      symbolAccount,
      futuresAccount,
      BtbIndexProxy: BtbIndex,
      marketInfo: preFindBtbResult,
      marketInfoList: marketInfoList || []
    })
    // 执行
    v.buySellMachine.execute()
  })
  // 找回止盈或者爆仓了的单子
  for (let i = 0; i < btbList.length; i++) {
    const btbItem = btbList[i]
    const buySellMachine = btbItem.buySellMachine
    if (buySellMachine.findStopExecuteOrder()) {
      await buySellMachine.requestCreateStopTradeRecord()
    }
  }
  // 排序
  btbList = sortBtbIndexList(btbList)
  // 执行买卖
  const hasPositionList = []
  for (let i = 0; i < btbList.length; i++) {
    const btbItem = btbList[i]
    const buySellMachine = btbItem.buySellMachine
    const symbolAccount = accountMachine.getBtbItemAccount(btbItem.symbol)
    const futuresAccount = accountMachine.getFuturesAccount()
    buySellMachine.resetAccount({
      symbolAccount,
      futuresAccount
    })
    let exResult = false
    if (hasPositionList.length < POSITION_LEN || buySellMachine.hasPosition) {
      // 没有买满，或者你本来就有仓位
      exResult = await buySellMachine.requestOrder()
    }
    await buySellMachine.requestUpdateBtbModel()
    if (exResult) {
      // 重新刷新账户
      await accountMachine.refresh()
      const newSymbolAccount = accountMachine.getBtbItemAccount(btbItem.symbol)
      if (newSymbolAccount.hasPosition) {
        hasPositionList.push(btbItem.code)
      }
    } else {
      // 没有操作
      if (symbolAccount.hasPosition) {
        hasPositionList.push(btbItem.code)
      }
    }
  }
  const downOrderEndTime = moment().format('YYYY-MM-DD HH:mm:ss')
  // 更新及时盈亏
  const time = btbAs.getFormatTradeTime(moment().format('YYYY-MM-DD HH:mm'), '15m')
  await ctx.services.executeRecord.addQuoteVolumeRecord(time, {
    total_margin_balance: accountMachine.getFuturesAccount().totalMarginBalance || 0,
    position_config: accountMachine.positionConfig
  })
  // 设置止盈止损，同是抓取了最新持仓信息
  for (let i = 0; i < btbList.length; i++) {
    const btbItem = btbList[i]
    const buySellMachine = btbItem.buySellMachine
    const symbolAccount = accountMachine.getBtbItemAccount(btbItem.symbol)
    const futuresAccount = accountMachine.getFuturesAccount()
    buySellMachine.resetAccount({
      symbolAccount,
      futuresAccount
    })
    // 防止10秒50单，睡0.2秒就可以限制住
    await sleep(200)
    await buySellMachine.createStopOrder()
  }
  // 保存最新持仓数量
  for (let i = 0; i < btbList.length; i++) {
    const btbItem = btbList[i]
    const buySellMachine = btbItem.buySellMachine
    await buySellMachine.updateBtbPosition()
    // 记录交易记录，放这里不去影响主流程
    await buySellMachine.requestCreateTradeRecord()
  }
  // p.print('结束')
  return {
    btbCount: btbList.length,
    btbPositionConfig,
    positionConfig: accountMachine.positionConfig,
    downOrderEndTime
  }
}
